use std::{env};
use std::fs::File;
use std::io::{BufReader};
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};




#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[allow(unused)]
enum Target {
    Ohos,
    Android,
    Ios,
}


fn main() {
    println!("cargo:rerun-if-env-changed=OH_NDK_ROOT");
    println!("cargo:rerun-if-env-changed=ArkUI_X_ROOT");

    let ndk_root = env::var("OH_NDK_ROOT");
    let ark_ui_root = env::var("ArkUI_X_ROOT");
    let ndk = match ndk_root {
        Ok(root_path) => {
            println!("OH_NDK_ROOT={}", root_path);
            oh_ndk_get_by_path(&PathBuf::from(root_path))
        }
        Err(_) => {
            let ndk_list = oh_ndk_default_install_list();

            let max = ndk_list.iter().max_by(|x, y| {
                x.api_version.cmp(&y.api_version)
            });
            max.expect("cannot find any ndk").clone()
        }
    };

    println!("[oh-napi-src] select: API[{}] in: {}", ndk.api_version, ndk.root_dir.display());

    println!("cargo:rustc-cfg=oh_api_version=\"{}\"", ndk.info.api_version);


    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
    let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();


    let os = if target_env.as_str() == "ohos" {
        Target::Ohos
    } else if target_os.as_str() == "android" {
        Target::Android
    } else if target_os.as_str() == "ios" {
        Target::Ios
    } else {
        println!("cargo:warning=[oh-napi-sys] [{}] 暂不支持", target_os);
        return;
    };


    // 需要 ArkUI-X
    if os == Target::Android || os == Target::Ios {
        if ndk.api_version < 10 {
            panic!("不支持 API 10 以下的版本");
        }

        let ark_ui_x = match ark_ui_root {
            Ok(root) => {
                arkui_x_get_by_path(&PathBuf::from(&root))
            }
            Err(_) => {
                let list = ark_ui_x_default_install_list();

                let max = list.iter().max_by(|x, y| {
                    let x_i = x.info.api_version.parse::<usize>().unwrap();
                    let y_i = y.info.api_version.parse().unwrap();
                    x_i.cmp(&y_i)
                });
                max.expect("cannot find any ArkUI-X").clone()
            }
        };
        println!("[oh-napi-src] select: ArkUI-X [{}] in: {}",
                 ark_ui_x.info.api_version, ark_ui_x.root_dir.display());


        if os == Target::Android {
            let dir_name = match target_arch.as_str() {
                "aarch64" => "android-arm64-release",
                "arm" => "android-arm-release",
                "x86_64" => "android-x86_64",
                _ => panic!("{} 不支持", target_arch),
            };
            let lib_dir = ark_ui_x.root_dir
                .join("engine")
                .join("lib")
                .join("arkui")
                .join(dir_name);
            println!("cargo:rustc-link-search={}", lib_dir.display());
            println!("cargo:rustc-link-lib=arkui_android");
        }
    } else {
        let dir_name = format!("{}-linux-ohos", target_arch);
        let lib_dir = ndk.root_dir
            .join("sysroot")
            .join("usr").join("lib").join(dir_name);
        println!("cargo:rustc-link-search={}", lib_dir.display());
        println!("cargo:rustc-link-lib=ace_napi.z");
    }

}

#[derive(Debug, Clone)]
struct NDK {
    api_version: usize,
    info: NdkInfo,
    root_dir: PathBuf,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct NdkInfo {
    #[serde(rename = "path")]
    path: String,

    #[serde(rename = "apiVersion")]
    api_version: String,

    #[serde(rename = "displayName")]
    display_name: String,

    #[serde(rename = "meta")]
    meta: Meta,

    #[serde(rename = "releaseType")]
    release_type: String,

    #[serde(rename = "version")]
    version: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
struct Meta {
    #[serde(rename = "metaVersion")]
    meta_version: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
struct ArkUIX {
    #[serde(rename = "path")]
    path: String,

    #[serde(rename = "apiVersion")]
    api_version: String,

    #[serde(rename = "displayName")]
    display_name: String,

    #[serde(rename = "meta")]
    meta: Meta,

    #[serde(rename = "releaseType")]
    release_type: String,

    #[serde(rename = "version")]
    version: String,
}

#[derive(Debug, Clone)]
struct ArkUIXInfo {
    info: ArkUIX,
    root_dir: PathBuf,
}


fn user_local_dir() -> PathBuf {
    let base_dirs = directories::BaseDirs::new().unwrap();
    base_dirs.data_local_dir().into()
}

fn oh_ndk_default_install_list() -> Vec<NDK> {
    let mut l = oh_ndk_huawei_list();
    l.extend(oh_ndk_oh_list());

    for one in &l {
        println!("[oh-napi-src] find: API[{}] --> {:?}", one.api_version, one);
    }

    l
}

fn ark_ui_x_default_install_list() -> Vec<ArkUIXInfo> {
    let base_dir = user_local_dir().join("ArkUI-X").join("Sdk");
    let mut out = vec![];

    if let Ok(dirs) = base_dir.read_dir() {
        for dir in dirs.flatten() {
            if dir.file_name().to_string_lossy().to_string().parse::<f32>().is_ok() {
                let root = dir.path().join("arkui-x");
                out.push(arkui_x_get_by_path(&root))
            }
        }
    }
    out
}

fn oh_ndk_huawei_list() -> Vec<NDK> {
    let base_dir = user_local_dir().join("Huawei").join("Sdk").join("openharmony");
    let ndk_dirs = oh_ndk_dirs_in_sdk(&base_dir);
    ndk_dirs.iter().map(|dir|
        oh_ndk_get_by_path(dir)
    ).collect()
}

fn oh_ndk_oh_list() -> Vec<NDK> {
    let base_dir = user_local_dir().join("OpenHarmony").join("Sdk");

    let ndk_dirs = oh_ndk_dirs_in_sdk(&base_dir);
    ndk_dirs.iter().map(|dir|
        oh_ndk_get_by_path(dir)
    ).collect()
}

fn oh_ndk_dirs_in_sdk(sdk: &Path) -> Vec<PathBuf> {
    let mut out = vec![];
    if let Ok(dir) = sdk.read_dir() {
        for d in dir.flatten() {
            let dir_name = d.file_name().to_string_lossy().to_string();
            if dir_name.parse::<f32>().is_ok() {
                out.push(d.path().join("native"))
            }
        }
    }

    out
}


fn arkui_x_get_by_path(path: &Path) -> ArkUIXInfo {
    let pkg_info = path.join("arkui-x.json");
    if let Ok(file) =
        File::open(pkg_info) {
        let reader = BufReader::new(file);
        let info: ArkUIX = serde_json::from_reader(reader).unwrap();
        return ArkUIXInfo {
            info,
            root_dir: path.into(),
        };
    }

    panic!("not ArkUI-X root dir: {}", path.display());
}

fn oh_ndk_get_by_path(path: &Path) -> NDK {
    let pkg_info = path.join("oh-uni-package.json");
    if let Ok(file) =
        File::open(pkg_info) {
        let reader = BufReader::new(file);
        let info: NdkInfo = serde_json::from_reader(reader).unwrap();
        return NDK {
            api_version: info.api_version.parse().unwrap(),
            info,
            root_dir: path.into(),
        };
    }

    panic!("not ndk root dir: {}", path.display());
}


